package com.splic.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.splic.common.core.DeviceContext;
import com.splic.common.exception.BusinessException;
import com.splic.common.utils.DateUtils;
import com.splic.common.utils.StringUtils;
import com.splic.product.domain.Product;
import com.splic.product.domain.UserSubRecord;
import com.splic.product.domain.Weather;
import com.splic.product.domain.WeatherIcon;
import com.splic.product.dto.WeatherRequestDto;
import com.splic.product.mapper.WeatherMapper;
import com.splic.product.service.ProductService;
import com.splic.product.service.UserSubRecordService;
import com.splic.product.service.WeatherIconService;
import com.splic.product.service.WeatherService;
import com.splic.product.service.impl.thirdRequest.PositioningCityRequestServiceImpl;
import com.splic.product.service.impl.thirdRequest.WeatherRequestServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 天气Service业务层处理
 * 
 * @author singe
 * @date 2022-01-04
 */
@Service
@Slf4j
public class WeatherServiceImpl implements WeatherService
{
    @Autowired
    private WeatherMapper weatherMapper;
    @Autowired
    private WeatherRequestServiceImpl weatherRequestServiceImpl;
    @Autowired
    private PositioningCityRequestServiceImpl positioningCityRequestService;
    @Autowired
    private WeatherIconService weatherIconServiceImpl;
    @Autowired
    private UserSubRecordService userSubRecordServiceImpl;
    @Autowired
    private ProductService productServiceImpl;

    /**
     * 查询天气
     * 
     * @param id 天气主键
     * @return 天气
     */
    @Override
    public Weather selectWeatherById(Long id)
    {
        return weatherMapper.selectWeatherById(id);
    }

    /**
     * 查询天气列表
     * 
     * @param weather 天气
     * @return 天气
     */
    @Override
    public List<Weather> selectPageList(Weather weather)
    {
        return weatherMapper.selectWeatherList(weather);
    }

    /**
     * 查询天气列表
     *
     * @param weather 天气
     * @return 天气
     */
    @Override
    public List<Weather> selectWeatherList(Weather weather)
    {
        return weatherMapper.selectWeatherList(weather);
    }

    /**
     * 新增天气
     * 
     * @param weather 天气
     * @return 结果
     */
    @Override
    public int insert(Weather weather)
    {
        weather.setCreateTime(DateUtils.getNowDate());
        return weatherMapper.insert(weather);
    }

    /**
     * 修改天气
     * 
     * @param weather 天气
     * @return 结果
     */
    @Override
    public int update(Weather weather)
    {
        weather.setUpdateTime(DateUtils.getNowDate());
        return weatherMapper.update(weather);
    }


    /**
     * 删除天气信息
     * 
     * @param id 天气主键
     * @return 结果
     */
    @Override
    public int deleteById(Long id)
    {
        return weatherMapper.deleteById(id);
    }
    /**
     * 修改天气
     *
     * @param weather 天气信息
     * @return 结果
     */
    @Override
    @Transactional
    public int saveOrUpdate(Weather weather)
    {
        if (StringUtils.isNotEmpty(weather.getTem()) && StringUtils.isNotEmpty(weather.getTem1()) && StringUtils.isNotEmpty(weather.getTem2())) {
            if (Integer.parseInt(weather.getTem()) > Integer.parseInt(weather.getTem1())) {
                weather.setTem1(weather.getTem());
            }
            if (Integer.parseInt(weather.getTem()) < Integer.parseInt(weather.getTem2())) {
                weather.setTem2(weather.getTem());
            }
        }
        if(weather.getId() != null){
            weather.setUpdateTime(new Date());
            return weatherMapper.update(weather);
        }else{
            weather.setCreateTime(new Date());
            weather.setUpdateTime(new Date());
            return weatherMapper.insert(weather);
        }
    }


    @Override
    public List<Weather> selectUserWeather(BigDecimal lng,BigDecimal lat,Long userId) {
        List<Weather> resultWeatherList = Lists.newArrayList();

        Product product = productServiceImpl.getByPackageKey(DeviceContext.getProductPackageKey());
        if (product == null){
            throw new BusinessException("应用查询失败。");
        }
        //FIXME 目前只有一个应用对应天气服务  此处goodsTypeId 暂时写死  待后续服务中增加类型之后 在扩展
        Long goodsTypeId = 6L;
        UserSubRecord userSubRecord = userSubRecordServiceImpl.selectUserSubRecord(userId,product.getId(),goodsTypeId);
        if (userSubRecord == null){
            throw new BusinessException("您还没有订阅天气服务");
        }
        if (Objects.equals(0,userSubRecord.getStatus())){
            throw new BusinessException("您的天气服务已过期");
        }

        //根据高德查询定位城市code
        String areaCode = positioningCityRequestService.requestThirdData(lng,lat);
        if(StringUtils.isEmpty(areaCode)){
            throw new BusinessException("坐标查询位置信息失败。");
        }

        List<Weather> weatherList = weatherMapper.selectWeatherByCountyCode(areaCode,DateUtils.dateTime(DateUtils.getCurrentDayStartTime()));
        if (CollectionUtils.isEmpty(weatherList) || weatherList.size() < 2){
            this.thirdTomorrowRequest(areaCode,resultWeatherList);
        }else{
            for(Weather weather:weatherList){
                if(DateUtils.isSameDay(new Date(),weather.getDate())){
                    //如果当前城市的今日的天气更新时间大于1小时 则重新请求三方获取新的数据
                    if (weather.getUpdateTime().before(DateUtils.addMinutes(new Date(),-30))
                        || StringUtils.isEmpty(weather.getTem())){

                        List<WeatherRequestDto.WeatherRequest> requestList = weatherRequestServiceImpl.requestThirdTomorrowData(areaCode);
                        if(CollectionUtils.isNotEmpty(requestList)){
                            for(WeatherRequestDto.WeatherRequest request:requestList){
                                //第一次查询时 当前的实时温度为空 则查询一次实时数据
                                if(DateUtils.isSameDay(new Date(),request.getDate())){
//                                    Weather weatherOld = weatherMapper.getByCountyCodeAndDate(areaCode,DateUtils.dateTime(request.getDate()));
//                                    if(weatherOld == null || StringUtils.isEmpty(weatherOld.getTem())){
                                    List<WeatherRequestDto.WeatherRequest> tempList = weatherRequestServiceImpl.requestThirdTodayData(areaCode);
                                    request.setWea(tempList.get(0).getWea());
                                    request.setTem(tempList.get(0).getTem());
                                    request.setWinSpeed(tempList.get(0).getWinSpeed());
                                    request.setWin(tempList.get(0).getWin());
                                    request.setAir(tempList.get(0).getAir());
//                                    }
                                    Weather weather1 = saveWeather(request,areaCode,request.getDate());
                                    resultWeatherList.add(weather1);
                                }
                            }
                        }
                    }else{
                        resultWeatherList.add(weather);
                    }
                }else{
                    //如果当前城市的明日的天气更新时间大于6小时 则重新请求三方获取新的数据
                    if (weather.getUpdateTime().before(DateUtils.addHours(new Date(),-6))){
                        resultWeatherList.clear();
                        this.thirdTomorrowRequest(areaCode,resultWeatherList);
                    }else{
                        resultWeatherList.add(weather);
                    }
                }
            }
        }



        //替换天气图标
        if(CollectionUtils.isNotEmpty(resultWeatherList)){
            for (Weather weather1 :resultWeatherList){
                WeatherIcon icon = weatherIconServiceImpl.getByName(weather1.getWea());
                if(icon != null){
                    weather1.setWeaImg(icon.getIcon());
                }
            }
        }
        try {
            log.info("====\n" + DeviceContext.getDeviceIdentify() + "====\n" + product.getName() +"====\n" + new Date() + "====\n" + lat + "====\n" + lng + "====\n" + areaCode + "====\n" + resultWeatherList.get(0) + "====\n" + resultWeatherList.get(1));
        } catch (Exception e) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(baos));
            String exception = baos.toString();
            log.error("baos:" + exception);
        }
        return resultWeatherList;
    }

    /**
     * 查询实时天气
     * @param areaCode
     * @param resultWeatherList
     */
    private void thirdTodayRequest(String areaCode,List<Weather> resultWeatherList){
        //查询三方  获取天气并存储
        List<WeatherRequestDto.WeatherRequest> requestList = weatherRequestServiceImpl.requestThirdTodayData(areaCode);
        if(CollectionUtils.isNotEmpty(requestList)){
            for(WeatherRequestDto.WeatherRequest request:requestList){
                Weather weather = saveWeather(request,areaCode,request.getDate());
                resultWeatherList.add(weather);
            }
        }
    }

    /**
     * 查询两天天气
     * @param areaCode
     * @param resultWeatherList
     */
    private void thirdTomorrowRequest(String areaCode,List<Weather> resultWeatherList){
        //查询三方  获取天气并存储
        List<WeatherRequestDto.WeatherRequest> requestList = weatherRequestServiceImpl.requestThirdTomorrowData(areaCode);
        if(CollectionUtils.isNotEmpty(requestList)){
            for(WeatherRequestDto.WeatherRequest request:requestList){
                //第一次查询时 当前的实时温度为空 则查询一次实时数据
                if(DateUtils.isSameDay(new Date(),request.getDate())){
                    Weather weatherOld = weatherMapper.getByCountyCodeAndDate(areaCode,DateUtils.dateTime(request.getDate()));
                    if(weatherOld == null || StringUtils.isEmpty(weatherOld.getTem())){
                        thirdTodayRequest(areaCode,Lists.newArrayList());
                    }
                }
                Weather weather = saveWeather(request,areaCode,request.getDate());
                resultWeatherList.add(weather);
            }
        }
    }

    @Override
    public void requestThirdData() {
        WeatherRequestDto dto = weatherRequestServiceImpl.requestThirdData();
        if(CollectionUtils.isNotEmpty(dto.getWeathersList())){
            Date currentDate = new Date();
            for(WeatherRequestDto.WeatherRequest weatherRequest:dto.getWeathersList()){
                try {
                    //saveWeather(weatherRequest,weatherRequest.getWeatherCityId(),currentDate);
                }catch(Exception e){
                    log.error("天气保存失败");
                }
            }
        }
    }

    private Weather saveWeather(WeatherRequestDto.WeatherRequest weatherRequest,String areaCode,Date currentDate) {
        Weather weatherOld = weatherMapper.getByCountyCodeAndDate(areaCode,DateUtils.dateTime(weatherRequest.getDate()));

        Weather weather = new Weather();
        weather.setWea(weatherRequest.getWea());
        if(StringUtils.isEmpty(weatherRequest.getTem()) && weatherOld !=null && StringUtils.isNotEmpty(weatherOld.getTem())){
            weather.setTem(weatherOld.getTem());
        }else{
            weather.setTem(weatherRequest.getTem());
        }
        if(StringUtils.isEmpty(weatherRequest.getTem1()) && weatherOld !=null && StringUtils.isNotEmpty(weatherOld.getTem1())){
            weather.setTem1(weatherOld.getTem1());
        }else{
            weather.setTem1(weatherRequest.getTem1());
        }
        if(StringUtils.isEmpty(weatherRequest.getTem2()) && weatherOld !=null && StringUtils.isNotEmpty(weatherOld.getTem2())) {
            weather.setTem2(weatherOld.getTem2());
        }else {
            weather.setTem2(weatherRequest.getTem2());
        }
        weather.setWin(weatherRequest.getWin());
        weather.setWinSpeed(weatherRequest.getWinSpeed());
        weather.setAir(weatherRequest.getAir());
        weather.setCountyCode(areaCode);
        weather.setDate(currentDate);
        weather.setWeek(weatherRequest.getWeek());
        if(weatherOld != null){
            weather.setId(weatherOld.getId());
        }
        this.saveOrUpdate(weather);
        return weather;
    }

    @Slf4j
    public static class VivoSign {
        /**
         * 根据传入的map，把map里的key value转换为接口的请求参数，并给参数按ascii码排序
         *
         * @param paramsMap 传入的map
         * @return 按ascii码排序的参数键值对拼接结果
         */
        public static String getUrlParamsFromMap(Map<String, Object> paramsMap) {
            List<String> keysList = new ArrayList<>(paramsMap.keySet());
            Collections.sort(keysList);
            StringBuilder sb = new StringBuilder();
            for (String key : keysList) {
                if ("sign".equals(key)) {
                    continue;
                }
                Object object = paramsMap.get(key);
                if (object == null) {
                    continue;
                }
                sb.append(key).append("=");
                if ("bizParam".equals(key)) {
                    sb.append(JSON.toJSONString(object)).append("&");
                } else {
                    sb.append(object).append("&");
                }
            }
            if (sb.length() > 0) {
                sb.deleteCharAt(sb.length() - 1);
            }
            return sb.toString();
        }
        /**
         * HMAC_SHA256 验签加密
         * */
        public static String HmacSHA256(String data, String key) {
            try {
                byte[] secretByte = key.getBytes(StandardCharsets.UTF_8);
                SecretKeySpec signingKey = new SecretKeySpec(secretByte, "HmacSHA256");
                Mac mac = Mac.getInstance("HmacSHA256");
                mac.init(signingKey);
                byte[] dataByte = data.getBytes(StandardCharsets.UTF_8);
                byte[] by = mac.doFinal(dataByte);
                return byteArr2HexStr(by);
            } catch (Exception e) {
                log.error("HmacSHA256异常，data={}", data, e);
            }
            return null;
        }
        private static String byteArr2HexStr(byte[] arrB) {
            int iLen = arrB.length;
            // 每个byte用两个字符才能表示，所以字符串的长度是数组长度的两倍
            StringBuilder sb = new StringBuilder(iLen * 2);
            for (int b : arrB) {
                int intTmp = b;
                // 把负数转换为正数
                while (intTmp < 0) {
                    intTmp = intTmp + 256;
                }
                // 小于0F的数需要在前面补0
                if (intTmp < 16) {
                    sb.append("0");
                }
                sb.append(Integer.toString(intTmp, 16));
            }
            return sb.toString();
        }
    }
}
